#include <compilation/task/PhiFrontierTask.h>


namespace compilation
{


void
LabelPhiFrontierAgenda::propagate(LabelInstruction& label)
{
  assert(label.mark);

  LabelResult& result = label.getMarkAs<LabelResult>();

  for (GotoInstruction* frontier : label.getDominationFrontier())
  {
    DEBUG_INSTRUCTION_VISITOR
        << "processing goto "
        << frontier->getRegistrationIndex()
        << std::endl;

    std::set<VirtualExpression*, IsBefore> modified;

    LabelResult& receiver =
        frontier->getSNextInstruction()->getMarkAs<LabelResult>();

    for (LabelResult::Map::value_type const& expr : result.map())
    {
      LocalVariableExpression const& current =
          expression_cast<TLocalVariable>(*expr.first);

      int index;
      Scope where;
      if (frontier->getScope()->find(current.name(), index, where) !=
          Scope::FindResult::FRNotFound) // $1
      {
        LabelResult::iterator found = receiver.map().find(expr.first);
        /**/ if (found == receiver.map().end()) // $2
        {
          std::pair<VirtualExpression*,
              std::pair<GotoInstruction*, GotoInstruction*> >
              insert;
          {
            insert.first = expr.first;
            insert.second.first = frontier;
            insert.second.second = nullptr;
          }

          found = receiver.map().insert(found, insert);

          if (modified.find(expr.first) == modified.end())
            modified.insert(expr.first);
        }
        else if ((found->second.first != frontier) &&
                 (found->second.second != frontier)) // $3
        {
          if (!found->second.first)
            found->second.first = frontier;
          else if (!found->second.second)
            found->second.second = frontier;
          else
          { assert(false); }
        }
      }
    }

    if (!modified.empty()) // $4
      addNewAsFirst(
            new LabelPhiFrontierTask(
              *instruction_cast<TLabel>(frontier->getSNextInstruction()),
              modified));
  }
}


void
LabelPhiFrontierAgenda::propagateOn(
    const LabelInstruction& label,
    const ModifiedVariables& originModified)
{
  for (GotoInstruction* frontierIter : label.getDominationFrontier())
  {
    std::set<VirtualExpression*, IsBefore> modified;

    LabelResult& receiver =
        frontierIter->getSNextInstruction()->getMarkAs<LabelResult>();

    for (VirtualExpression* exprIter : originModified)
    {
      LocalVariableExpression& current =
          expression_cast<TLocalVariable>(*exprIter);

      int index;
      Scope where;
      if (receiver.getScope().find(current.name(), index, where) !=
          Scope::FindResult::FRNotFound) // $1
      {
        LabelResult::iterator found = receiver.map().find(exprIter);
        /**/ if (found == receiver.map().end()) // $2
        {
          std::pair<VirtualExpression*,
              std::pair<GotoInstruction*, GotoInstruction*> >
              insert;
          {
            insert.first = exprIter;
            insert.second.first = frontierIter;
            insert.second.second = nullptr;
          }

          found = receiver.map().insert(found, insert);

          if (modified.find(exprIter) == modified.end())
            modified.insert(exprIter);
        }
        else if ((found->second.first != frontierIter) &&
                 (found->second.second != frontierIter)) // $3
        {
          if (!found->second.first)
            found->second.first = frontierIter;
            else if (!found->second.second)
            found->second.second = frontierIter;
          else
          { assert(false); }
        }
      }
    }

    if (!modified.empty()) // $4
      addNewAsFirst(
            new LabelPhiFrontierTask(
              *instruction_cast<TLabel>(frontierIter->getSNextInstruction()),
              modified));
  }
}


void compilation::LabelPhiFrontierInstructionHandler<TLabel>::operator ()(VirtualInstruction* instr, LabelPhiFrontierTask& task, LabelPhiFrontierAgenda& continuations, WorkListReusability& reuse)
{
  LabelInstruction* gt = instruction_cast<TLabel>(instr);

  continuations.propagateOn(*gt, task.getModifiedVariables());

  //    base_handler::operator ()(instr,task,continuations,reuse);
}


} // compilation
